using System;
using System.IO;
using System.Net;
using System.Data;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace BonzerGate.PaymentGateways.AuthorizeNet {
  /// <summary>
  /// Enumeration for transaction method (Credit Card | Electronic Check)
  /// </summary>
  public enum TransactionMethod : int {
    /// <summary>
    /// Represents a credit card transaction
    /// </summary>
    CC = 1,
    /// <summary>
    /// Represents an electronic check transaction
    /// </summary>
    ECHECK = 2
  }
  /// <summary>
  /// Enumeration for the transaction type (AUTH_CAPTURE | AUTH_ONLY | CAPTURE_ONLY | VOID | PRIOR_AUTH_CAPTURE | CREDIT)
  /// </summary>
  public enum TransactionType : int {
    /// <summary>
    /// Authorizes and Settles the transaction.
    /// </summary>
    AUTH_CAPTURE = 1,
    /// <summary>
    /// Authorizes the transaction to be settled at a later time.
    /// </summary>
    AUTH_ONLY = 2,
    /// <summary>
    /// Settles transaction from a previously authorized transaction.
    /// </summary>
    CAPTURE_ONLY = 3,
    /// <summary>
    /// Voids a transaction that has not yet been settled.
    /// </summary>
    VOID = 4,
    /// <summary>
    /// Selects a transaction that has not settled.
    /// </summary>
    PRIOR_AUTH_CAPTURE = 5,
    /// <summary>
    /// Credits the account back from a previously settled transaction.
    /// </summary>
    CREDIT = 6
  }

  /// <summary>
  /// This is the methods used / needed for AuthorizeNet to run correctly. This is what
  /// AuthorizeNet refers to as the AIM (Advanced Integration Method) and is not intended
  /// for simple or novis use. This module can be plugged into other .Net 2 web-applications
  /// or can be used with actual solo machine applications.
  /// </summary>
  public sealed class AuthNetAIM {
    /// <summary>
    /// Used for Garbage Collection and Disposing Methods
    /// </summary>
    private bool disposed = false;
    /// <summary>
    /// This DataTable hold validation information for Properties. This is used to help return information back to 
    /// the code so information can be displayed to the user.
    /// </summary>
    public readonly DataTable dtError = new DataTable("error");
    /// <summary>
    /// Used to get Customer Billing Information.
    /// </summary>
    private CustomerBillingInfo customerBillingInfo = null;

    #region "CLASS SPECIFIC UTILIZATION VARIABLES & PROPERTIES"

    /// <summary>
    /// Indicates whether to validate shipping information.
    /// </summary>
    private bool isShippingRequired = false;
    /// <summary>
    /// Gets or Sets whether to validate shipping information.
    /// </summary>
    public bool is_shipping_required {
      get { return isShippingRequired; }
      set { isShippingRequired = value; }
    }
    /// <summary>
    /// Represents the URL where the gateway will process the request.
    /// </summary>
    private string gatewayURL = "https://secure.authorize.net/gateway/transact.dll";
    /// <summary>
    /// Gets or Sets the Gateway URL for AuthorizeNet.
    /// </summary>
    public string gateway_url {
      get { return gatewayURL; }
      set {
        if (value == "" || value == null) { gatewayURL = "https://secure.authorize.net/gateway/transact.dll"; }
        else { gatewayURL = value; }
      }
    }

    #endregion

    #region "MERCHANT ACCOUNT INFORMATION"

    /// <summary>
    /// (Optional) Default:3.1 - Can either be 2.5, 3, 3.1. 3.1 Supports all the latest features and is recommended.
    /// </summary>
    private string xversion = "3.1";
    /// <summary>
    /// (Optional) Default:TRUE - Indicates whether the transaction is a test or a live transaction
    /// </summary>
    private string xtest_request = "FALSE";

    #endregion

    #region "GATEWAY RESPONSE CONFIGURATION"
    /// <summary>
    /// Gets the value of the gateway delimit data.
    /// </summary>
    public readonly string x_delim_data = "TRUE";
    /// <summary>
    /// Gets the value of the delimiter character.
    /// </summary>
    public readonly string x_delim_char = "|";
    /// <summary>
    /// Gets the value of the encapsulation character.
    /// </summary>
    public readonly string x_encap_char = "";
    /// <summary>
    /// Gets the value of the relay response. For all AIM transactions this must be = FALSE.
    /// </summary>
    public readonly string x_relay_response = "FALSE";
    /// <summary>
    /// Gets the value of submitting duplicate transactions in seconds. 60 seconds is the default.
    /// </summary>
    public readonly string x_duplicate_window = "60";

    #endregion

    #region "ADDITIONAL CUSTOMER INFORMATION"

    /// <summary>
    /// (Optional) Represents the customer's ID. Usually their database id for easy tracking.
    /// </summary>
    private string xcust_id = "";
    /// <summary>
    /// Gets or Sets the customer's ID that will be passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_cust_id {
      get { return xcust_id; }
      set { xcust_id = value; }
    }
    /// <summary>
    /// (Optional) Indicates the origin of the customer's ip address at transaction time.
    /// </summary>
    private string xcustomer_ip = "";
    /// <summary>
    /// Gets or Sets the customer's IP Address that will be passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_customer_ip {
      get { return xcustomer_ip; }
      set { xcustomer_ip = value; }
    }
    /// <summary>
    /// (Optional) Represents the Tax ID or the customer's SSN.
    /// </summary>
    private string xcustomer_tax_id = "";
    /// <summary>
    /// Gets or Sets the customer's Tax ID or SSN that will be passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_customer_tax_id {
      get { return xcustomer_tax_id; }
      set { xcustomer_tax_id = value; }
    }


    #endregion

    #region "EMAIL SETTINGS"

    /// <summary>
    /// (Optional) A valid e-mail address for the customer
    /// </summary>
    private string xemail = "";
    /// <summary>
    /// Gets or Sets the customer's email address that will be passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_email {
      get { return xemail; }
      set { xemail = value; }
    }
    /// <summary>
    /// (Optional) Determines whether a confirmation email will be sent to the customer from AuthorizeNet.
    /// </summary>
    private string xemail_customer = "TRUE";
    /// <summary>
    /// Gets or Sets whether the customer recieves a confirmation e-mail from AuthorizeNet. Default = "TRUE"
    /// </summary>
    public string x_email_customer {
      get { return xemail_customer; }
      set {
        if (value.ToUpper() == "TRUE" || value.ToUpper() == "FALSE") { xemail_customer = value.ToUpper(); }
        else { xemail_customer = "TRUE"; }
      }
    }
    /// <summary>
    /// (Optional) An e-mail value to send the merchant a copy of the reciept confirmation.
    /// </summary>
    private string xmerchant_email = "";
    /// <summary>
    /// Gets or Sets the merchant e-mail recipient for the confirmation reciept. Default is set in the gateway settings.
    /// </summary>
    public string x_merchant_email {
      get { return xmerchant_email; }
      set { xmerchant_email = value; }
    }

    #endregion

    #region "INVOICE INFORMATION"

    /// <summary>
    /// (Optional) Represents custom merchant invoice number for the transaction.
    /// </summary>
    private string xinvoice_num = "";
    /// <summary>
    /// Gets or Sets the invoice number passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_invoice_num {
      get { return xinvoice_num; }
      set { xinvoice_num = value; }
    }
    /// <summary>
    /// (Optional) Represents a short description for the transaction.
    /// </summary>
    private string xdescription = "";
    /// <summary>
    /// Gets or Sets the description for the transaction that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_description {
      get { return xdescription; }
      set { xdescription = value; }
    }

    #endregion

    #region "SHIPPING INFORMATION"

    /// <summary>
    /// First Name where the products / services will be shipped
    /// </summary>
    private string xship_to_first_name = "";
    /// <summary>
    /// Last Name where the products / services will be shipped
    /// </summary>
    private string xship_to_last_name = "";
    /// <summary>
    /// (Optional) Company where the products / services will be shipped
    /// </summary>
    private string xship_to_company = "";
    /// <summary>
    /// Primary Billing Address where the products / services will be shipped
    /// </summary>
    private string xship_to_address = "";
    /// <summary>
    /// City where the products / services will be shipped
    /// </summary>
    private string xship_to_city = "";
    /// <summary>
    /// State / Province where the products / services will be shipped
    /// </summary>
    private string xship_to_state = "";
    /// <summary>
    /// Country where the products / services will be shipped
    /// </summary>
    private string xship_to_country = "";
    /// <summary>
    /// Zip Code where the products / services will be shipped
    /// </summary>
    private string xship_to_zip = "";

    #endregion

    #region "TRANSACTION DATA"

    /// <summary>
    /// (Required) The full amount of the transaction. The transaction must be greater than or equal to $1
    /// </summary>
    private string xamount = "";
    /// <summary>
    /// Gets or Sets the transaction amount passed to the AuthorizeNet Gateway.
    /// </summary>
    /// <exception cref="System.Exception">Throws System.Exception on an invalid amount.</exception>
    public string x_amount {
      get { return xamount; }
      set {
        if (value.Length > 1 && value != null) { xamount = value; }
        else { throw new Exception("x_amount must contain a value greater than $1 and cannont be null."); }
      }
    }
    /// <summary>
    /// (Optional) Represents a 2 or 3 letter abbreviation for a country to adjust the currency type.
    /// </summary>
    /// <remarks>The default is "US"</remarks>
    private string xcurrency_code = "USD";
    /// <summary>
    /// Gets or Sets the currency code for the transaction passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_currency_code {
      get { return xcurrency_code; }
      set {
        if (value != "" || value != null) { xcurrency_code = value; } // Values to be validated in later revisions
        else { xcurrency_code = "USD"; }
      }
    }
    /// <summary>
    /// (Optional) Default:AUTH_CAPTURE - Determines what type of transaction the gateway issues. Valid values are AUTH_CAPTURE,
    /// </summary>
    /// <remarks>Valid values are AUTH_CAPTURE, AUTH_ONLY, CAPTURE_ONLY, CREDIT, VOID, PRIOR_AUTH_CAPTURE. With some of these values other properties need to be populated.</remarks>
    public readonly string x_type = "";
    /// <summary>
    /// (Optional) Default:CC - Determines whether a credit card transaction or an electronic check transaction is to take place.
    /// </summary>
    /// <remarks>Valid values are CC, ECHECK. Depending on which is passed certain properties need to be entered in.</remarks>
    public readonly string x_method = "CC";
    /// <summary>
    /// (Required if x_type = ECHECK) Determines whether the transaction is recurring or not.
    /// </summary>
    /// <remarks>Default value = NO</remarks>
    private string xrecurring_billing = "NO";
    /// <summary>
    /// Gets or Sets the recurring billing for an ECHECK type transaction that is passed to the AuthorizeNet Gateway.
    /// </summary>
    /// <remarks>This has a default value of "NO"</remarks>
    public string x_recurring_billing {
      get { return xrecurring_billing; }
      set {
        if (value != "" || value != null) {
          string x_recurring = value.ToUpper();
          if (x_recurring == "NO" || x_recurring == "YES") { xrecurring_billing = x_recurring; }
          else { xrecurring_billing = "NO"; }
        }
        else { xrecurring_billing = "NO"; }
      }
    }
    /// <summary>
    /// (Required if x_type = "ECHECK") Represents the bank's routing number.
    /// </summary>
    private string xbank_aba_code = "";
    /// <summary>
    /// Gets or Sets the bank routing number of the customer.
    /// </summary>
    /// <remarks>Routing numbers are usually 10 characters in length.</remarks>
    /// <exception cref="System.Exception">If the x_type = "ECHECK" this expection will be thrown on an indvalid Routing Number</exception>
    public string x_bank_aba_code {
      get { return xbank_aba_code; }
      set {
        if (x_method == "ECHECK") {
          if ((value != "" || value != null) && value.Length == 10) { xbank_aba_code = value; }
          else { throw new Exception("x_bank_aba_code is not valid for this type of transaction."); }
        }
        else {
          xbank_aba_code = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "ECHECK") Represents the bank account number of the customer.
    /// </summary>
    private string xbank_acct_num = "";
    /// <summary>
    /// Gets or Sets the bank account number of the customer.
    /// </summary>
    public string x_bank_acct_num {
      get { return xbank_acct_num; }
      set {
        if (x_method == "ECHECK") {
          if (value != "" || value != null) { xbank_acct_num = value; }
          else { throw new Exception("x_bank_acct_num is not valid for this type of transaction."); }
        }
        else {
          xbank_acct_num = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "ECHECK") Represents the bank account type of the customer.
    /// </summary>
    /// <remarks>Values can be SAVINGS, CHECKING, BUSINESSCHECKING. Default is CHECKING</remarks>
    private string xbank_acct_type = "CHECKING";
    /// <summary>
    /// Gets or Sets the bank account type of the customer
    /// </summary>
    /// <remarks>Values can be SAVINGS, CHECKING, BUSINESSCHECKING. Default is CHECKING</remarks>
    public string x_bank_acct_type {
      get { return xbank_acct_type; }
      set {
        if (x_method == "ECHECK") {
          if (value != "" || value != null) {
            string x_acct_type = value.ToUpper();
            if (x_acct_type == "CHECKING" || x_acct_type == "BUSINESSCHECKING" || x_acct_type == "SAVINGS") {
              xbank_acct_type = x_acct_type;
            }
            else { xbank_acct_type = "CHECKING"; }
          }
          else { xbank_acct_type = "CHECKING"; }
        }
        else {
          xbank_acct_type = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "ECHECK") Represents the bank's name.
    /// </summary>
    private string xbank_name = "";
    /// <summary>
    /// Gets or Sets the customer's bank name.
    /// </summary>
    /// <example><code>authNet.x_bank_name = "Bank of America";</code></example>
    public string x_bank_name {
      get { return xbank_name; }
      set {
        if (x_method == "ECHECK") {
          if (value != "" || value != null) { xbank_name = value; }
          else { throw new Exception("x_bank_name is not valid for this type of transaction."); }
        }
        else {
          xbank_name = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "ECHECK") Represents the account holder's name.
    /// </summary>
    private string xbank_acct_name = "";
    /// <summary>
    /// Gets or Sets the account holder's name.
    /// </summary>
    public string x_bank_acct_name {
      get { return xbank_name; }
      set {
        if (x_method == "ECHECK") {
          if (value != "" || value != null) { xbank_acct_name = value; }
          else { throw new Exception("x_bank_acct_name is not valid for this type of transaction."); }
        }
        else {
          xbank_acct_name = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "ECHECK") Represents the account holder's name.
    /// </summary>
    private string xecheck_type = "WEB";
    /// <summary>
    /// Gets or Sets the electronic check type.
    /// </summary>
    /// <remarks>Default value = "WEB". Valid values are PPD, CCD, TEL, WEB</remarks>
    public string x_echeck_type {
      get { return xecheck_type; }
      set {
        if (x_method == "ECHECK") {
          string echeck_type = value.ToUpper();
          if (echeck_type == "WEB" || echeck_type == "PPD" || echeck_type == "CCD" || echeck_type == "TEL") {
            xecheck_type = echeck_type;
          }
          else {
            xecheck_type = "WEB";
          }
        }
        else {
          xecheck_type = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "CC") Represents the credit card number
    /// </summary>
    private string xcard_num = "";
    /// <summary>
    /// Gets or Sets the credit card number.
    /// </summary>
    /// <remarks>Both Set / Get take into account readable formatting for both AuthNet and display.</remarks>
    public string x_card_num {
      get {
        try {
          if (xcard_num.StartsWith("3") && xcard_num.Length == 15) {
            return String.Format("{0:#### ###### #####}", long.Parse(xcard_num));
          }
          else {
            if (xcard_num.Length == 16) {
              return String.Format("{0:#### #### #### ####}", long.Parse(xcard_num));
            }
            return xcard_num;
          }
        }
        catch (FormatException f) {
          return xcard_num;
        }
      }
      set {
        if (x_method == "CC") {
          string formatted_card = value;
          formatted_card = formatted_card.Replace(" ", "");
          formatted_card = formatted_card.Replace("/", "");
          formatted_card = formatted_card.Replace("\\", "");
          formatted_card = formatted_card.Replace("-", "");
          formatted_card = formatted_card.Replace(".", "");

          if (formatted_card.Length == 15 && formatted_card.StartsWith("3")) {
            xcard_num = formatted_card;
          }
          else if ((formatted_card.StartsWith("6") || formatted_card.StartsWith("4") || formatted_card.StartsWith("5")) && formatted_card.Length == 16) {
            xcard_num = formatted_card;
          }
          else { DataRow dr = dtError.NewRow(); dr[0] = "(Credit Card Number) is not a valid length for its type. (ex. Visa 16 numbers, Amex 15 Numbers)"; dtError.Rows.Add(dr); }
        }
        else {
          xcard_num = null;
        }
      }
    }
    /// <summary>
    /// (Required if x_type = "CC") Represents the credit card number
    /// </summary>
    private string xexp_date = "";
    /// <summary>
    /// Gets or Sets the credit card's expiration date.
    /// </summary>
    public string x_exp_date {
      get { return xexp_date; }
      set {
        if (x_method == "CC") {
          if (value.Length >= 4) { xexp_date = value; } else { DataRow dr = dtError.NewRow(); dr[0] = "(Expiration Date) is required and cannot be empty."; dtError.Rows.Add(dr); }
        }
        else {
          xexp_date = null;
        }
      }
    }
    /// <summary>
    /// (Optional) 3 - 4 Digit code located on most credit cards
    /// </summary>
    private string xcard_code = "";
    /// <summary>
    /// Gets or Sets the credit card's 3 - 4 digit code
    /// </summary>
    public string x_card_code {
      get { return xcard_code; }
      set {
        if (x_method == "CC") {
          if (value.Length == 3 || value.Length == 4) {
            xcard_code = value;
          }
          else {
            DataRow dr = dtError.NewRow(); dr[0] = "(Signature Code) is required and cannot be empty."; dtError.Rows.Add(dr);
          }
        }
      }
    }
    /// <summary>
    /// Represents the transaction id from AuthorizeNet
    /// </summary>
    private string xtrans_id = "";
    /// <summary>
    /// Gets or Sets the transaction id passed to / from AuthorizeNet Gateway.
    /// </summary>
    public string x_trans_id {
      get { return xtrans_id; }
      set {
        if (x_type == "CREDIT" || x_type == "VOID" || x_type == "PRIOR_AUTH_CAPTURE") {
          if (value != null || value != "") { xtrans_id = value; }
          else { throw new Exception("x_trans_id is needed for this transaction type."); }
        }
        else {
          xtrans_id = null;
        }
      }
    }
    /// <summary>
    /// Represents the authorization code for the transaction
    /// </summary>
    private string xauth_code = "";
    /// <summary>
    /// Gets or Sets the authorization code of the transaction
    /// </summary>
    public string x_auth_code {
      get { return xauth_code; }
      set {
        if (x_method == "CAPTURE_ONLY") {
          if (value != null || value != "") { xauth_code = value; }
          else { throw new Exception("x_auth_code is required for this transaction method."); }
        }
        else {
          xauth_code = null;
        }
      }
    }

    /*
     *  not implemented as of this release
        private string xauthentication_indicator = "";
        private string chAuthValue = "";
    */

    #endregion

    #region "LEVEL 2 DATA"

    /*
             *  not implemented as of this release
                private string poNum = "";
                private string taxAmount = "";
                private string isTaxExempt = "FALSE";
                private string freightAmount = "";
                private string dutyAmount = "";
            */

    #endregion

    #region "AIM MERCHANT ACCOUNT PROPERTIES"

    /// <summary>
    /// (Required) Login Username given by AuthorizeNet
    /// </summary>
    /// <remarks>This value is a required parameter of all constructors.</remarks>
    /// <exception cref="System.Exception">Exception will be encountered if this value is left empty or null.</exception>
    public readonly string x_login = "";
    /// <summary>
    /// (Optional) Password used to sign into AuthorizeNet online services
    /// </summary>
    /// <remarks>This value is only evaluated if passed to a constructor that requires it.</remarks>
    /// <exception cref="System.Exception">Exeption will be encountered when value is empty or null through a constructor that requires it.</exception>
    public readonly string x_password = "";
    /// <summary>
    /// (Optional) Transaction Key is a feature set through the AuthorizeNet website
    /// </summary>
    /// <remarks>This features value / requiredness is set through the online settings.</remarks>
    /// <exception cref="System.Exception">Exception will be encountered if this value is empty or null when passed to a constructor that requires it.</exception>
    public readonly string x_tran_key = "";
    /// <summary>
    /// Indicates what AuthorizeNet transaction version to use. The default is 3.1.
    /// </summary>
    public string x_version {
      get { return xversion; }
      set {
        if (value == "3" || value == "3.1" || value == "2.5") { xversion = value; }
        else { xversion = "3.1"; }
      }
    }
    /// <summary>
    /// Idicates whether the transaction is a Test transaction. Default is (FALSE) or live transaction.
    /// </summary>
    public string x_test_request {
      get { return xtest_request; }
      set {
        if (value.ToUpper() == "FALSE" || value.ToUpper() == "TRUE") { xtest_request = value.ToUpper(); }
        else { xtest_request = "FALSE"; }
      }
    }

    #endregion

    #region "AIM SHIPPING PROPERTIES"

    /// <summary>
    /// Gets or Sets customer's first name for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_first_name {
      get { return xship_to_first_name; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_first_name = value; }
          else { throw new Exception("x_ship_to_first_name is a required field and cannot be an empty or a null value.\n You may turn off this requirement by setting AuthorizeNet.is_shipping_required = false"); }
        }
        else {
          if (value == null) { xship_to_first_name = ""; }
          else { xship_to_first_name = value; }
        }
      }
    }
    /// <summary>
    /// Gets or Sets customer's last name for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_last_name {
      get { return xship_to_last_name; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_last_name = value; }
          else { throw new Exception("x_ship_to_last_name is a required field and cannot be an empty or a null value.\n You may turn off this requirement by setting AuthorizeNet.is_shipping_required = false"); }
        }
        else {
          if (value == null) { xship_to_last_name = ""; }
          else { xship_to_last_name = value; }
        }
      }
    }
    /// <summary>
    /// Gets or Sets customer's company for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_company {
      get { return xship_to_company; }
      set {
        if (value != null) { xship_to_company = value; }
        else { xship_to_company = ""; }
      }
    }
    /// <summary>
    /// Gets or Sets customer's address for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_address {
      get { return xship_to_address; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_address = value; }
          else { throw new Exception("x_ship_to_address is a required field and cannot be an empty or a null value.\n You may turn off this requirement by setting AuthorizeNet.is_shipping_required = false"); }
        }
        else {
          if (value == null) { xship_to_address = ""; }
          else { xship_to_address = value; }
        }
      }
    }
    /// <summary>
    /// Gets or Sets customer's city for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_city {
      get { return xship_to_city; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_city = value; }
          else { throw new Exception("x_ship_to_city is a required field and cannot be an empty or a null value.\n You may turn off this requirement by setting AuthorizeNet.is_shipping_required = false"); }
        }
        else {
          if (value == null) { xship_to_city = ""; }
          else { xship_to_city = value; }
        }
      }
    }
    /// <summary>
    /// Gets or Sets customer's state for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_state {
      get { return xship_to_state; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_state = value; }
          else { throw new Exception("x_ship_to_state is a required field and cannot be an empty or a null value.\n You may turn off this requirement by setting AuthorizeNet.is_shipping_required = false"); }
        }
        else {
          if (value == null) { xship_to_state = ""; }
          else { xship_to_state = value; }
        }
      }
    }
    /// <summary>
    /// Gets or Sets customer's zip code for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_zip {
      get { return xship_to_zip; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_zip = value; }
          else { throw new Exception("x_ship_to_city is a required field and cannot be an empty or a null value.\n You may turn off this requirement by setting AuthorizeNet.is_shipping_required = false"); }
        }
        else {
          if (value == null) { xship_to_zip = ""; }
          else { xship_to_zip = value; }
        }
      }
    }
    /// <summary>
    /// Gets or Sets customer's country for shipping information that is passed to the AuthorizeNet Gateway.
    /// </summary>
    public string x_ship_to_country {
      get { return xship_to_country; }
      set {
        if (isShippingRequired) {
          if (value != "" && value != null) { xship_to_country = value; }
          else { xship_to_country = ""; }
        }
        else {
          if (value == null) { xship_to_country = ""; }
          else { xship_to_country = value; }
        }
      }
    }

    #endregion

    #region "Consructor Methods for AuthorizeNet"
    /// <summary>
    /// 
    /// </summary>
    /// <param name="xlogin"></param>
    /// <param name="xpassword"></param>
    /// <param name="xtran_key"></param>
    /// <param name="TxnMethod"></param>
    /// <param name="TxnType"></param>
    /// <param name="BillingInformation"></param>
    public AuthNetAIM(string xlogin, string xpassword, string xtran_key, TransactionMethod TxnMethod, TransactionType TxnType, CustomerBillingInfo BillingInformation) {
      dtError.Columns.Add("UserError", Type.GetType("System.String"));

      if (xlogin != "" && xlogin != null) { x_login = xlogin; }
      else { throw new Exception("xlogin was passed to the constructor with a null or an empty value"); }

      if (xpassword != "" && xpassword != null) { x_password = xpassword; }
      else { throw new Exception("xpassword was passed to the constructor with a null or an empty value."); }

      if (xtran_key != "" && xtran_key != null) { x_tran_key = xtran_key; }
      else { throw new Exception("xtrankey was passed to the constructor with a null or an empty value."); }

      if (BillingInformation == null) { throw new Exception("Customer Billing Information was passed in with a null reference."); }
      else { this.customerBillingInfo = BillingInformation; }

      x_method = TxnMethod.ToString();
      x_type = TxnType.ToString();
    }
    #endregion

    #region "RETURNING METHODS TO SETUP THE TRANSACTION STRING"
    /// <summary>
    /// Creates the Post String that will be sent
    /// </summary>
    /// <returns>A valid AuthorizeNet post string</returns>
    private string create_transaction_string() {
      string s = "";

      // Merchant Information String Setup
      s += "x_login=" + x_login;
      if (x_password.Length > 0) { s += "&x_password=" + x_password; }
      if (x_tran_key.Length > 0) { s += "&x_tran_key=" + x_tran_key; }
      s += "&x_version=" + xversion;
      s += "&x_test_request=" + xtest_request;

      // Gateway Response Configuration String Setup
      s += "&x_delim_data=" + x_delim_data;
      s += "&x_delim_char=" + x_delim_char;
      s += "&x_encap_char=" + x_encap_char;
      s += "&x_relay_response=" + x_relay_response;
      s += "&x_duplicate_window=" + x_duplicate_window;

      // Transaction Information String Setup
      s += "&x_method=" + x_method;
      s += "&x_type=" + x_type;
      s += "&x_amount=" + x_amount;
      s += "&x_currency_code=" + xcurrency_code;
      s += "&x_trans_id=" + xtrans_id;
      s += "&x_auth_code=" + xauth_code;

      // Determine type of transaction and format string accordingly
      if (x_method == "CC") {
        s += "&x_card_num=" + xcard_num;
        s += "&x_exp_date=" + xexp_date;
        s += "&x_card_code=" + xcard_code;
      }
      else {
        s += "&x_recurring_billing=" + xrecurring_billing;
        s += "&x_bank_acct_name=" + xbank_acct_name;
        s += "&x_bank_name=" + xbank_name;
        s += "&x_bank_aba_code=" + xbank_aba_code;
        s += "&x_bank_acct_num=" + xbank_acct_num;
        s += "&x_bank_acct_type=" + xbank_acct_type;
        s += "&x_echeck_type=" + xecheck_type;
      }

      // E-Mail Settings String Setup
      s += "&x_email=" + xemail;
      s += "&x_email_customer=" + xemail_customer;
      s += "&x_merchant_email=" + xmerchant_email;

      // Invoice Information String Setup
      s += "&x_invoice_num=" + xinvoice_num;
      s += "&x_description=" + xdescription;

      // Additional Customer Information
      s += "&x_cust_id=" + xcust_id;
      s += "&x_customer_ip=" + xcustomer_ip;
      s += "&x_customer_tax_id=" + xcustomer_tax_id;

      // Customer Billing Information
      s += "&x_first_name=" + customerBillingInfo.FirstName;
      s += "&x_last_name=" + customerBillingInfo.LastName;
      s += "&x_company=" + customerBillingInfo.CompanyName;
      s += "&x_address=" + customerBillingInfo.StreetAddress;
      s += "&x_city=" + customerBillingInfo.City;
      s += "&x_state=" + customerBillingInfo.StateProvince;
      s += "&x_zip=" + customerBillingInfo.ZipCode;
      s += "&x_country=" + customerBillingInfo.Country;
      s += "&x_phone=" + customerBillingInfo.PrimaryPhoneNumber;
      s += "&x_fax=" + customerBillingInfo.FaxNumber;

      // Customer Shipping Information
      s += "&x_ship_to_first_name=" + xship_to_first_name;
      s += "&x_ship_to_last_name=" + xship_to_last_name;
      s += "&x_ship_to_company=" + xship_to_company;
      s += "&x_ship_to_address=" + xship_to_address;
      s += "&x_ship_to_city=" + xship_to_city;
      s += "&x_ship_to_state=" + xship_to_state;
      s += "&x_ship_to_zip=" + xship_to_zip;
      s += "&x_ship_to_country=" + xship_to_country;

      // Return the information
      return s;
    }
    /// <summary>
    /// Posts transaction information to the AuthorizeNet Gateway.
    /// </summary>
    /// <returns>System.DataTable containing the information for the transaction</returns>
    public DataTable get_transaction_response() {

      DataTable dt = new DataTable("AuthNetResponse");
      dt.Columns.Add("ResponseField", Type.GetType("System.String"));
      dt.Columns.Add("ResponseCode", Type.GetType("System.String"));
      dt.Columns.Add("ResponseMessage", Type.GetType("System.String"));

      string delimited_response = "";

      try {
        if (dtError.Rows.Count > 0) {
          return dtError;
        }
        else {
          HttpWebRequest authRequest = (HttpWebRequest)WebRequest.Create(gatewayURL);
          authRequest.Method = "POST";
          authRequest.ContentLength = create_transaction_string().Length;
          authRequest.ContentType = "application/x-www-form-urlencoded";

          using (StreamWriter sw = new StreamWriter(authRequest.GetRequestStream())) {
            sw.Write(create_transaction_string());
            sw.Close();
          }

          HttpWebResponse authResponse = (HttpWebResponse)authRequest.GetResponse();
          using (StreamReader sr = new StreamReader(authResponse.GetResponseStream())) {
            delimited_response = sr.ReadToEnd();
            sr.Close();
          }

          string[] response = delimited_response.Split(char.Parse(x_delim_char));

          DataRow dr = dt.NewRow();
          dr[0] = "ResponseCode";
          dr[1] = response[0];
          dr[2] = response[3];
          xtrans_id = response[6];
          xauth_code = response[4];
          dt.Rows.Add(dr);

          return dt;
        }
      }
      catch (Exception e) {
        DataRow dr = dt.NewRow();
        dr[0] = "ResponseCode";
        dr[1] = "4";
        dr[2] = e.Message;
        dt.Rows.Add(dr);

        return dt;
      }
    }
    #endregion

    #region "CLEAN UP METHODS"

    /// <summary>
    /// Destructor to release unmanaged resources by this class.
    /// </summary>
    ~AuthNetAIM() {
      Dispose(false);
    }
    /// <summary>
    /// Releases managed and unmanaged resources.
    /// </summary>
    public void Dispose() {
      Dispose(true);
    }
    /// <summary>
    /// Internal dispose function which releases managed and unmanaged resources.
    /// </summary>
    /// <param name="disposing">true if managed and unmanaged are being disposed : unmanaged only.</param>
    private void Dispose(bool disposing) {
      if (!this.disposed) {
        //clean out Error Holding DataTable
        dtError.Dispose();
      }
      this.disposed = true;
    }

    #endregion
  }
}